<template>
  <div class="properties-converter">
    <div class="page-header">
      <h1>Properties 转换器</h1>
      <p>Properties、YAML、XML格式互相转换</p>
    </div>
    
    <div class="converter-container">
      <div class="input-section">
        <div class="format-selector">
          <label>输入格式:</label>
          <div class="format-buttons">
            <button 
              v-for="format in formats" 
              :key="format.key"
              @click="inputFormat = format.key"
              :class="['format-btn', { active: inputFormat === format.key }]"
            >
              {{ format.label }}
            </button>
          </div>
        </div>
        
        <div class="input-group">
          <label for="input-text">输入内容:</label>
          <textarea 
            id="input-text"
            v-model="inputText" 
            :placeholder="getPlaceholder(inputFormat)"
            rows="12"
          ></textarea>
        </div>
        
        <div class="button-group">
          <button @click="convertFormat" class="btn btn-primary" :disabled="!inputText.trim()">
            <span class="btn-icon">🔄</span>
            转换格式
          </button>
          <button @click="clearAll" class="btn btn-outline">
            <span class="btn-icon">🗑️</span>
            清空
          </button>
        </div>
      </div>
      
      <div class="output-section">
        <div class="format-selector">
          <label>输出格式:</label>
          <div class="format-buttons">
            <button 
              v-for="format in formats" 
              :key="format.key"
              @click="outputFormat = format.key"
              :class="['format-btn', { active: outputFormat === format.key }]"
            >
              {{ format.label }}
            </button>
          </div>
        </div>
        
        <div class="output-group">
          <label for="output-text">输出结果:</label>
          <textarea 
            id="output-text"
            v-model="outputText" 
            placeholder="转换结果将显示在这里..."
            rows="12"
            readonly
          ></textarea>
        </div>
        
        <div class="action-group">
          <button @click="copyResult" class="btn btn-success" :disabled="!outputText">
            <span class="btn-icon">📋</span>
            复制结果
          </button>
          <button @click="swapFormats" class="btn btn-secondary">
            <span class="btn-icon">🔄</span>
            交换格式
          </button>
        </div>
      </div>
    </div>
    
    <div class="info-section">
      <div class="info-card">
        <h3>支持的格式</h3>
        <ul>
          <li><strong>Properties:</strong> Java属性文件格式，key=value形式</li>
          <li><strong>YAML:</strong> 人类可读的数据序列化标准</li>
          <li><strong>XML:</strong> 可扩展标记语言格式</li>
        </ul>
        
        <h3>使用说明</h3>
        <ul>
          <li>选择输入格式，粘贴或输入要转换的内容</li>
          <li>选择目标输出格式</li>
          <li>点击转换按钮获得结果</li>
          <li>支持嵌套结构和数组格式</li>
        </ul>
      </div>
      
      <div class="example-card">
        <h3>格式示例</h3>
        <div class="example-tabs">
          <button 
            v-for="format in formats" 
            :key="format.key"
            @click="activeExample = format.key"
            :class="['example-tab', { active: activeExample === format.key }]"
          >
            {{ format.label }}
          </button>
        </div>
        
        <div class="example-content">
          <pre><code>{{ getExample(activeExample) }}</code></pre>
        </div>
        
        <button @click="loadExample" class="btn btn-outline btn-small">
          <span class="btn-icon">📝</span>
          加载示例
        </button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import * as yaml from 'js-yaml'

const inputText = ref('')
const outputText = ref('')
const inputFormat = ref('properties')
const outputFormat = ref('yaml')
const activeExample = ref('properties')

const formats = [
  { key: 'properties', label: 'Properties' },
  { key: 'yaml', label: 'YAML' },
  { key: 'xml', label: 'XML' }
]

const examples = {
  properties: `# 应用配置
app.name=MyApplication
app.version=1.0.0
app.debug=true

# 数据库配置
db.host=localhost
db.port=3306
db.username=admin
db.password=secret

# 服务器配置
server.port=8080
server.context-path=/api`,
  
  yaml: `# 应用配置
app:
  name: MyApplication
  version: 1.0.0
  debug: true

# 数据库配置
db:
  host: localhost
  port: 3306
  username: admin
  password: secret

# 服务器配置
server:
  port: 8080
  context-path: /api`,
  
  xml: `<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <app>
    <name>MyApplication</name>
    <version>1.0.0</version>
    <debug>true</debug>
  </app>
  <db>
    <host>localhost</host>
    <port>3306</port>
    <username>admin</username>
    <password>secret</password>
  </db>
  <server>
    <port>8080</port>
    <context-path>/api</context-path>
  </server>
</configuration>`
}

const getPlaceholder = (format: string) => {
  const placeholders = {
    properties: '请输入Properties格式内容...\n\n示例：\napp.name=MyApp\napp.version=1.0\ndb.host=localhost',
    yaml: '请输入YAML格式内容...\n\n示例：\napp:\n  name: MyApp\n  version: 1.0\ndb:\n  host: localhost',
    xml: '请输入XML格式内容...\n\n示例：\n<?xml version="1.0"?>\n<config>\n  <app>\n    <name>MyApp</name>\n  </app>\n</config>'
  }
  return placeholders[format as keyof typeof placeholders] || ''
}

const getExample = (format: string) => {
  return examples[format as keyof typeof examples] || ''
}

const loadExample = () => {
  inputText.value = getExample(activeExample.value)
  inputFormat.value = activeExample.value
}

const parseProperties = (text: string): Record<string, any> => {
  const result: Record<string, any> = {}
  const lines = text.split('\n')
  
  for (const line of lines) {
    const trimmed = line.trim()
    if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!')) {
      continue
    }
    
    const equalIndex = trimmed.indexOf('=')
    if (equalIndex === -1) continue
    
    const key = trimmed.substring(0, equalIndex).trim()
    const value = trimmed.substring(equalIndex + 1).trim()
    
    // 处理嵌套属性
    const keys = key.split('.')
    let current = result
    
    for (let i = 0; i < keys.length - 1; i++) {
      if (!current[keys[i]]) {
        current[keys[i]] = {}
      }
      current = current[keys[i]]
    }
    
    // 尝试转换数据类型
    let parsedValue: any = value
    if (value === 'true') parsedValue = true
    else if (value === 'false') parsedValue = false
    else if (!isNaN(Number(value)) && value !== '') parsedValue = Number(value)
    
    current[keys[keys.length - 1]] = parsedValue
  }
  
  return result
}

const objectToProperties = (obj: Record<string, any>, prefix = ''): string => {
  const lines: string[] = []
  
  for (const [key, value] of Object.entries(obj)) {
    const fullKey = prefix ? `${prefix}.${key}` : key
    
    if (value && typeof value === 'object' && !Array.isArray(value)) {
      lines.push(...objectToProperties(value, fullKey).split('\n').filter(line => line.trim()))
    } else {
      lines.push(`${fullKey}=${value}`)
    }
  }
  
  return lines.join('\n')
}

const parseXML = (xmlString: string): Record<string, any> => {
  try {
    const parser = new DOMParser()
    const xmlDoc = parser.parseFromString(xmlString, 'text/xml')
    
    // 检查解析错误
    const parseError = xmlDoc.querySelector('parsererror')
    if (parseError) {
      throw new Error('XML解析错误: ' + parseError.textContent)
    }
    
    const convertNode = (node: Element): any => {
      const result: any = {}
      
      // 处理属性
      if (node.attributes.length > 0) {
        result['@attributes'] = {}
        for (let i = 0; i < node.attributes.length; i++) {
          const attr = node.attributes[i]
          result['@attributes'][attr.name] = attr.value
        }
      }
      
      // 处理子节点
      const children = Array.from(node.children)
      if (children.length === 0) {
        // 叶子节点，返回文本内容
        const textContent = node.textContent?.trim() || ''
        if (Object.keys(result).length === 0) {
          return textContent
        } else {
          result['#text'] = textContent
          return result
        }
      }
      
      // 处理子元素
      const childGroups: Record<string, Element[]> = {}
      children.forEach(child => {
        if (!childGroups[child.tagName]) {
          childGroups[child.tagName] = []
        }
        childGroups[child.tagName].push(child)
      })
      
      Object.entries(childGroups).forEach(([tagName, elements]) => {
        if (elements.length === 1) {
          result[tagName] = convertNode(elements[0])
        } else {
          result[tagName] = elements.map(el => convertNode(el))
        }
      })
      
      return result
    }
    
    return convertNode(xmlDoc.documentElement)
  } catch (error) {
    throw new Error('XML解析失败: ' + (error instanceof Error ? error.message : '未知错误'))
  }
}

const objectToXML = (obj: any, rootName = 'root'): string => {
  const escapeXML = (str: string): string => {
    return str.replace(/&/g, '&amp;')
              .replace(/</g, '&lt;')
              .replace(/>/g, '&gt;')
              .replace(/"/g, '&quot;')
              .replace(/'/g, '&apos;')
  }
  
  const convertValue = (value: any, key: string, indent = ''): string => {
    if (value === null || value === undefined) {
      return `${indent}<${key}></${key}>`
    }
    
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
      return `${indent}<${key}>${escapeXML(String(value))}</${key}>`
    }
    
    if (Array.isArray(value)) {
      return value.map(item => convertValue(item, key, indent)).join('\n')
    }
    
    if (typeof value === 'object') {
      const attributes: string[] = []
      const children: string[] = []
      let textContent = ''
      
      Object.entries(value).forEach(([k, v]) => {
        if (k === '@attributes' && typeof v === 'object') {
          Object.entries(v as Record<string, any>).forEach(([attrName, attrValue]) => {
            attributes.push(`${attrName}="${escapeXML(String(attrValue))}"`)
          })
        } else if (k === '#text') {
          textContent = escapeXML(String(v))
        } else {
          children.push(convertValue(v, k, indent + '  '))
        }
      })
      
      const attrString = attributes.length > 0 ? ' ' + attributes.join(' ') : ''
      
      if (children.length === 0 && !textContent) {
        return `${indent}<${key}${attrString}></${key}>`
      } else if (children.length === 0 && textContent) {
        return `${indent}<${key}${attrString}>${textContent}</${key}>`
      } else {
        const childrenString = children.join('\n')
        return `${indent}<${key}${attrString}>\n${childrenString}\n${indent}</${key}>`
      }
    }
    
    return `${indent}<${key}>${escapeXML(String(value))}</${key}>`
  }
  
  const result = convertValue(obj, rootName)
  return `<?xml version="1.0" encoding="UTF-8"?>\n${result}`
}

const convertFormat = async () => {
  if (!inputText.value.trim()) {
    alert('请输入要转换的内容')
    return
  }
  
  if (inputFormat.value === outputFormat.value) {
    outputText.value = inputText.value
    return
  }
  
  try {
    let data: any
    
    // 解析输入格式
    switch (inputFormat.value) {
      case 'properties':
        data = parseProperties(inputText.value)
        break
      case 'yaml':
        data = yaml.load(inputText.value)
        break
      case 'xml':
        data = parseXML(inputText.value)
        break
      default:
        throw new Error('不支持的输入格式')
    }
    
    // 转换为输出格式
    switch (outputFormat.value) {
      case 'properties':
        outputText.value = objectToProperties(data)
        break
      case 'yaml':
        outputText.value = yaml.dump(data, { indent: 2, lineWidth: -1 })
        break
      case 'xml':
        outputText.value = objectToXML(data)
        break
      default:
        throw new Error('不支持的输出格式')
    }
    
  } catch (error) {
    console.error('转换失败:', error)
    alert(`转换失败: ${error instanceof Error ? error.message : '未知错误'}`)
  }
}

const clearAll = () => {
  inputText.value = ''
  outputText.value = ''
}

const copyResult = async () => {
  if (!outputText.value) return
  
  try {
    await navigator.clipboard.writeText(outputText.value)
    alert('结果已复制到剪贴板')
  } catch (error) {
    // 降级方案
    const textArea = document.createElement('textarea')
    textArea.value = outputText.value
    document.body.appendChild(textArea)
    textArea.select()
    document.execCommand('copy')
    document.body.removeChild(textArea)
    alert('结果已复制到剪贴板')
  }
}

const swapFormats = () => {
  const temp = inputFormat.value
  inputFormat.value = outputFormat.value
  outputFormat.value = temp
  
  // 如果有输出结果，将其移到输入框
  if (outputText.value) {
    inputText.value = outputText.value
    outputText.value = ''
  }
}
</script>

<style scoped>
.properties-converter {
  padding: 2rem;
  max-width: 1400px;
  margin: 0 auto;
}

.page-header {
  text-align: center;
  margin-bottom: 2rem;
}

.page-header h1 {
  color: #2c3e50;
  margin-bottom: 0.5rem;
  font-size: 2rem;
}

.page-header p {
  color: #7f8c8d;
  font-size: 1.1rem;
}

.converter-container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
  margin-bottom: 2rem;
}

.input-section,
.output-section {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  border: 1px solid #e1e8ed;
}

.format-selector {
  margin-bottom: 1rem;
}

.format-selector label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 600;
  color: #2c3e50;
}

.format-buttons {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.format-btn {
  padding: 0.5rem 1rem;
  border: 2px solid #e1e8ed;
  background: white;
  border-radius: 6px;
  font-size: 0.9rem;
  cursor: pointer;
  transition: all 0.2s;
  color: #7f8c8d;
}

.format-btn:hover {
  border-color: #3498db;
  color: #3498db;
}

.format-btn.active {
  background: #3498db;
  border-color: #3498db;
  color: white;
}

.input-group,
.output-group {
  margin-bottom: 1rem;
}

label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 600;
  color: #2c3e50;
}

textarea {
  width: 100%;
  padding: 0.75rem;
  border: 2px solid #e1e8ed;
  border-radius: 8px;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  resize: vertical;
  transition: border-color 0.2s;
}

textarea:focus {
  outline: none;
  border-color: #3498db;
}

textarea[readonly] {
  background-color: #f8f9fa;
  cursor: default;
}

.button-group,
.action-group {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.btn {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border: none;
  border-radius: 8px;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  text-decoration: none;
}

.btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn-primary:hover:not(:disabled) {
  background: #2980b9;
}

.btn-secondary {
  background: #95a5a6;
  color: white;
}

.btn-secondary:hover:not(:disabled) {
  background: #7f8c8d;
}

.btn-success {
  background: #27ae60;
  color: white;
}

.btn-success:hover:not(:disabled) {
  background: #229954;
}

.btn-outline {
  background: transparent;
  color: #7f8c8d;
  border: 2px solid #e1e8ed;
}

.btn-outline:hover:not(:disabled) {
  background: #f8f9fa;
  border-color: #bdc3c7;
}

.btn-small {
  padding: 0.5rem 0.75rem;
  font-size: 0.8rem;
}

.btn-icon {
  font-size: 1rem;
}

.info-section {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2rem;
}

.info-card,
.example-card {
  background: white;
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  border: 1px solid #e1e8ed;
}

.info-card h3,
.example-card h3 {
  color: #2c3e50;
  margin-bottom: 1rem;
  font-size: 1.2rem;
}

.info-card ul {
  list-style: none;
  padding: 0;
  margin-bottom: 1.5rem;
}

.info-card li {
  margin-bottom: 0.75rem;
  padding-left: 1rem;
  position: relative;
  color: #5a6c7d;
  line-height: 1.5;
}

.info-card li::before {
  content: '•';
  color: #3498db;
  position: absolute;
  left: 0;
}

.example-tabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 1rem;
}

.example-tab {
  padding: 0.5rem 0.75rem;
  border: none;
  background: #f8f9fa;
  border-radius: 6px 6px 0 0;
  font-size: 0.8rem;
  cursor: pointer;
  transition: all 0.2s;
  color: #7f8c8d;
}

.example-tab:hover {
  background: #e8f4fd;
  color: #3498db;
}

.example-tab.active {
  background: #3498db;
  color: white;
}

.example-content {
  background: #f8f9fa;
  border-radius: 0 8px 8px 8px;
  padding: 1rem;
  margin-bottom: 1rem;
  border: 1px solid #e1e8ed;
}

.example-content pre {
  margin: 0;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 0.8rem;
  line-height: 1.4;
  color: #2c3e50;
  white-space: pre-wrap;
  word-wrap: break-word;
}

@media (max-width: 768px) {
  .converter-container,
  .info-section {
    grid-template-columns: 1fr;
  }
  
  .format-buttons {
    justify-content: center;
  }
  
  .button-group,
  .action-group {
    justify-content: center;
  }
}

@media (prefers-color-scheme: dark) {
  .page-header h1 {
    color: #ecf0f1;
  }
  
  .page-header p {
    color: #bdc3c7;
  }
  
  .input-section,
  .output-section,
  .info-card,
  .example-card {
    background: #34495e;
    border-color: #4a5f7a;
  }
  
  .format-selector label,
  label {
    color: #ecf0f1;
  }
  
  .format-btn {
    background: #2c3e50;
    border-color: #4a5f7a;
    color: #bdc3c7;
  }
  
  .format-btn:hover {
    border-color: #3498db;
    color: #3498db;
  }
  
  textarea {
    background: #2c3e50;
    color: #ecf0f1;
    border-color: #4a5f7a;
  }
  
  textarea[readonly] {
    background-color: #2c3e50;
  }
  
  textarea:focus {
    border-color: #3498db;
  }
  
  .info-card h3,
  .example-card h3 {
    color: #ecf0f1;
  }
  
  .info-card li {
    color: #bdc3c7;
  }
  
  .example-tab {
    background: #2c3e50;
    color: #bdc3c7;
  }
  
  .example-tab:hover {
    background: #3d566e;
    color: #3498db;
  }
  
  .example-content {
    background: #2c3e50;
    border-color: #4a5f7a;
  }
  
  .example-content pre {
    color: #ecf0f1;
  }
  
  .btn-outline {
    color: #bdc3c7;
    border-color: #4a5f7a;
  }
  
  .btn-outline:hover:not(:disabled) {
    background: #2c3e50;
    border-color: #7f8c8d;
  }
}
</style>